//tcc version 0.9.27 (i386 Windows)

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

enum _ErrorCode {
	ERR_WRONG_ARG = 1,
	ERR_WROGN_ARG_TYPE,
	ERR_FAILURE_OPEN_FILE,

};
typedef enum _ErrorCode ErrorCode;

void help_info(void) {
	//TODO: 使用getopt 加入参数， 使length ，start 能独立存在， 不依赖于参数位置。比如 [-l <length>] [-s <start>]

	printf("hex_dump <file_path (string)> [length (uint) [start (uint)] ] \n"
		   "         file_path - path to the target file.\n"
		   "         length    - count of bytes which should be dumped.\n"
		   "         start     - start position of the file pointer.\n");
}

//将一个整数转换成类似 12.5 kb 的形式， 小数部分精确度不高。 1kb = 1024 byte， 以此类推。
double bytes_unit_parse(size_t num, char **unit_mark) {
	char *units[] = {"byte", "kb", "mb", "gb", "tb", "pb", "eb", };
	const int units_count = 7;

	double dnum = num * 1.0;
	double tmp = dnum;
	for (int i = 0; i < units_count; i++) {
		tmp /= 1024;
		if (tmp < 1) {
			*unit_mark = units[i];
			return dnum;
		}

		dnum = tmp;
	}

	*unit_mark = units[units_count - 1];
	return dnum;
}

int main(int argc, char const *argv[]) {
	if (argc < 2) {
		printf("Param Error, need <file_path (string)>\n" );
		help_info();
		return (int) ERR_WRONG_ARG;
	}

	char const *file_path = NULL;
	size_t length = 0;    //count of bytes should be printed
	bool has_length = false;
	size_t start = 0;    //start position of file pointer
	bool has_start = false;

	//Arguments Handling
	if (argc > 1) {
		if (argv[1] == "-h") {
			help_info();
			return 0;
		}

		file_path = argv[1];
	}

	char *endptr;
	if (argc > 2) {
		has_length = true;
		length = strtoul(argv[2], &endptr, 10);
		if (*endptr != '\0') {
			printf("Param Error, need [length] as unsigned long.\n");
			return (int) ERR_WROGN_ARG_TYPE;
		}
	}

	if (argc > 3) {
		has_start = true;
		start = strtoul(argv[3], &endptr, 10);
		if (*endptr != '\0') {
			printf("Param Error, need [start] as unsigned long.\n");
			return (int) ERR_WROGN_ARG_TYPE;
		}	
	}

	FILE* f = fopen(file_path, "rb");
	if (f == NULL) {
		printf("Failed To Open File.\n");
		return (int) ERR_FAILURE_OPEN_FILE;
	}

	if (has_start) {
		fseek(f, start, SEEK_CUR);
	}

	//classic and simple file reading style.
	size_t count = 0;
	int ch = 0;
	printf("{");
	while((ch = fgetc(f) ) != EOF) {
		printf("0x%x, ", ch);
		count++;

		if (has_length && count == length)
			break; 
	}
	printf("}\n\n");

	char *unit_mark = NULL;
	double bytes_d = 0;
	bytes_d = bytes_unit_parse(count, &unit_mark);
	printf("Dumped Length : %u => %.2lf %s\n", count, bytes_d, unit_mark);
	
	if (has_start)
		printf("Start At : %u\n", start);
	
	//print total file size only if dumped length != total file size
	if (has_length) {
		fseek(f, 0, SEEK_END);
		unsigned long file_size = ftell(f);
		rewind(f);
		
		bytes_d = bytes_unit_parse(file_size, &unit_mark);
		printf("Total File Size : %u => %.2lf %s\n", file_size, bytes_d, unit_mark);
	}

	fclose(f);

	return 0;
}